;;; ps-vector: vector utilities for Pre-Scheme
;;;
;;; These routines are based on SRFI-43 for Scheme, with some
;;; adjustments to account for the limitations of Pre-Scheme.
;;;
;;; Pre-Scheme's native vectors don't support vector-length at runtime,
;;; so we take an additional length argument, as is common practice in C.
;;;
;;; Pre-Scheme doesn't support variadic functions, so we have a variant
;;; for each arity, as you might do in C.  It should be possible to
;;; generate these with a macro, but that's not yet implemented.

;;; vector-unfold

(define-syntax vector-unfold
  (syntax-rules ()
    ((_ proc len)
     (vector-unfold0 proc len))
    ((_ proc len seed)
     (vector-unfold1 proc len seed))
    ((_ proc len seed1 seed2)
     (vector-unfold2 proc len seed1 seed2))
    ((_ proc len seed1 seed2 seed3)
     (vector-unfold3 proc len seed1 seed2 seed3))))

(define (vector-unfold0 proc len)
  ;; FIXME get proc's return type without calling it
  (let ((result (make-vector len (proc 0))))
    (let loop ((i 0))
      (if (= i len)
          result
          (begin
            (vector-set! result i (proc i))
            (loop (+ i 1)))))))

(define (vector-unfold1 proc len seed)
  (let ((result (receive (val next)
                    (proc 0 seed)
                  (make-vector len val))))
    (let loop ((i 0) (seed seed))
      (if (= i len)
          result
          (receive (val next)
              (proc i seed)
            (vector-set! result i val)
            (loop (+ i 1) next))))))

(define (vector-unfold2 proc len seed1 seed2)
  (let ((result (receive (val next1 next2)
                    (proc 0 seed1 seed2)
                  (make-vector len val))))
    (let loop ((i 0) (seed1 seed1) (seed2 seed2))
      (if (= i len)
          result
          (receive (val next1 next2)
              (proc i seed1 seed2)
            (vector-set! result i val)
            (loop (+ i 1) next1 next2))))))

(define (vector-unfold3 proc len seed1 seed2 seed3)
  (let ((result (receive (val next1 next2 next3)
                    (proc 0 seed1 seed2 seed3)
                  (make-vector len val))))
    (let loop ((i 0) (seed1 seed1) (seed2 seed2) (seed3 seed3))
      (if (= i len)
          result
          (receive (val next1 next2 next3)
              (proc i seed1 seed2 seed3)
            (vector-set! result i val)
            (loop (+ i 1) next1 next2 next3))))))

;;; vector-fold

(define-syntax vector-fold
  (syntax-rules ()
    ((_ proc init vec len)
     (vector-fold1 proc init vec len))
    ((_ proc init vec1 len1 vec2 len2)
     (vector-fold2 proc init vec1 len1 vec2 len2))
    ((_ proc init vec1 len1 vec2 len2 vec3 len3)
     (vector-fold3 proc init vec1 len1 vec2 len2 vec3 len3))))

(define (vector-fold1 proc init vec len)
  (let loop ((i 0) (result init))
    (if (= i len)
        result
        (loop (+ i 1) (proc i result (vector-ref vec i))))))

(define (vector-fold2 proc init vec1 len1 vec2 len2)
  (let ((len (min len1 len2)))
    (let loop ((i 0) (result init))
      (if (= i len)
          result
          (loop (+ i 1) (proc i result
                              (vector-ref vec1 i)
                              (vector-ref vec2 i)))))))

(define (vector-fold3 proc init vec1 len1 vec2 len2 vec3 len3)
  (let ((len (min len1 len2 len3)))
    (let loop ((i 0) (result init))
      (if (= i len)
          result
          (loop (+ i 1) (proc i result
                              (vector-ref vec1 i)
                              (vector-ref vec2 i)
                              (vector-ref vec3 i)))))))

;;; vector-map!

(define-syntax vector-map!
  (syntax-rules ()
    ((_ proc vec len)
     (vector-map1! proc vec len))
    ((_ proc vec1 len1 vec2 len2)
     (vector-map2! proc vec1 len1 vec2 len2))
    ((_ proc vec1 len1 vec2 len2 vec3 len3)
     (vector-map3! proc vec1 len1 vec2 len2 vec3 len3))))

(define (vector-map1! proc vec len)
  (vector-fold (lambda (i vec val)
                 (vector-set! vec i (proc i val))
                 vec)
               vec vec len))

(define (vector-map2! proc vec1 len1 vec2 len2)
  (vector-fold (lambda (i vec val1 val2)
                 (vector-set! vec i (proc i val1 val2))
                 vec)
               vec1 vec1 len1 vec2 len2))

(define (vector-map3! proc vec1 len1 vec2 len2 vec3 len3)
  (vector-fold (lambda (i vec val1 val2 val3)
                 (vector-set! vec i (proc i val1 val2 val3))
                 vec)
               vec1 vec1 len1 vec2 len2 vec3 len3))

;;; vector-map1

(define-syntax vector-map
  (syntax-rules ()
    ((_ proc vec len)
     (vector-map1 proc vec len))
    ((_ proc vec1 len1 vec2 len2)
     (vector-map2 proc vec1 len1 vec2 len2))
    ((_ proc vec1 len1 vec2 len2 vec3 len3)
     (vector-map3 proc vec1 len1 vec2 len2 vec3 len3))))

(define (vector-map1 proc vec len)
  ;; FIXME get proc's return type without calling it
  (let ((res (make-vector len (proc 0 (vector-ref vec 0)))))
    (vector-fold (lambda (i res val)
                   (vector-set! res i (proc i val))
                   res)
                 res vec len)))

(define (vector-map2 proc vec1 len1 vec2 len2)
  (let* ((len (min len1 len2))
         (res (make-vector len (proc 0
                                     (vector-ref vec1 0)
                                     (vector-ref vec2 0)))))
    (vector-fold (lambda (i res val1 val2)
                   (vector-set! res i (proc i val1 val2))
                   res)
                 res vec1 len1 vec2 len2)))

(define (vector-map3 proc vec1 len1 vec2 len2 vec3 len3)
  (let* ((len (min len1 len2 len3))
         (res (make-vector len (proc 0
                                     (vector-ref vec1 0)
                                     (vector-ref vec2 0)
                                     (vector-ref vec3 0)))))
    (vector-fold (lambda (i res val1 val2 val3)
                   (vector-set! res i (proc i val1 val2 val3))
                   res)
                 res vec1 len1 vec2 len2 vec3 len3)))

;;; vector-for-each

(define-syntax vector-for-each
  (syntax-rules ()
    ((_ proc vec len)
     (vector-for-each1 proc vec len))
    ((_ proc vec1 len1 vec2 len2)
     (vector-for-each2 proc vec1 len1 vec2 len2))
    ((_ proc vec1 len1 vec2 len2 vec3 len3)
     (vector-for-each3 proc vec1 len1 vec2 len2 vec3 len3))))

(define (vector-for-each1 proc vec len)
  (vector-fold (lambda (i res val)
                 (proc i val)
                 res)
               (unspecific) vec len))

(define (vector-for-each2 proc vec1 len1 vec2 len2)
  (vector-fold (lambda (i res val1 val2)
                 (proc i val1 val2)
                 res)
               (unspecific) vec1 len1 vec2 len2))

(define (vector-for-each3 proc vec1 len1 vec2 len2 vec3 len3)
  (vector-fold (lambda (i res val1 val2 val3)
                 (proc i val1 val2 val3)
                 res)
               (unspecific) vec1 len1 vec2 len2 vec3 len3))
